﻿#region

using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;
using Vin.Tool.AspNetCore.Singleton;

#endregion

namespace Vin.Tool.Core.AssemblyCategory;

/// <summary>
/// 程序集
/// </summary>
public class AssemblyTool
{
    #region 项目中的程序集

    private static List<Assembly> ProjectAssemblies { get; set; } = new();

    /// <summary>
    /// 获取项目中所有项目的程序集
    /// </summary>
    public static List<Assembly> GetProjectAssemblies => ProjectAssemblies.Count > 0
        ? ProjectAssemblies
        // : ProjectAssemblies = GetAssemblies(lib => !lib.Serviceable && lib.Type != "package");
        : ProjectAssemblies = GetAssemblies(lib => !lib.Serviceable);
    // && lib.Type == "project"

    private static List<Type> ProjectTypes { get; set; } = new();

    public static List<Type> GetProjectTypes => ProjectTypes.Count > 0
        ? ProjectTypes
        : ProjectTypes = GetProjectAssemblies.SelectMany(m => m.GetTypes()).ToList();

    #endregion

    #region 所有Vin相关的程序集

    private static List<Assembly> VinAssemblies { get; set; } = new();

    // /// <summary>
    // /// 获取所有程序集
    // /// </summary>
    // public static List<Assembly> GetVinAssemblies => VinAssemblies.Count > 0
    //     ? VinAssemblies
    //     : VinAssemblies = GetProjectAssemblies.Concat(GetAssemblies("Vin.")).Concat(GetAssemblies("VinBen."))
    //         .Concat(GetAssemblies(VinApp.VinInitBuilderConfig.AssemblyNames)).ToList();
    /// <summary>
    /// 获取所有程序集
    /// </summary>
    public static List<Assembly> GetVinAssemblies => VinAssemblies.Count > 0
        ? VinAssemblies
        : VinAssemblies = GetProjectAssemblies.Concat(GetAssemblies("Vin.")).Concat(GetAssemblies("VinBen."))
            .Concat(GetAssemblies(VinApp.VinInitBuilderConfig.AssemblyNames))
            .Concat(GetAssembliesByDirectory(VinApp.VinInitBuilderConfig.AssemblyPaths))
            .Distinct()
            .ToList();

    private static List<Type> VinTypes { get; set; } = new();

    public static List<Type> GetVinTypes => VinTypes.Count > 0
        ? VinTypes
        : VinTypes = GetVinAssemblies.SelectMany(m => m.GetTypes()).Distinct().ToList();

    #endregion

    #region 获取程序集

    /// <summary>
    /// 获取指定目录下的所有程序集
    /// </summary>
    /// <param name="directory"></param>
    /// <param name="searchOption"></param>
    /// <returns></returns>
    public static List<Assembly> GetAssembliesByDirectory(string directory,
        SearchOption searchOption = SearchOption.AllDirectories)
    {
        var list = new List<Assembly>();
        if (!Directory.Exists(directory))
        {
            return list;
        }

        var files = Directory.GetFiles(directory, "*.dll", searchOption);
        foreach (var file in files)
        {
            try
            {
                var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
                if (list.Contains(assembly)) continue;
                list.Add(assembly);
            }
            catch (Exception)
            {
                // ignored
            }
        }

        return list;
    }

    /// <summary>
    /// 获取指定目录下的所有程序集
    /// </summary>
    /// <param name="directorys"></param>
    /// <param name="searchOption"></param>
    /// <returns></returns>
    public static List<Assembly> GetAssembliesByDirectory(List<string> directorys,
        SearchOption searchOption = SearchOption.AllDirectories)
    {
        var list = new List<Assembly>();
        foreach (var directory in directorys)
        {
            list.AddRange(GetAssembliesByDirectory(directory, searchOption));
        }

        return list.Distinct().ToList();
    }

    /// <summary>
    /// 获取指定内容的程序集
    /// </summary>
    /// <param name="func"></param>
    /// <returns></returns>
    public static List<Assembly> GetAssemblies(Func<RuntimeLibrary, bool>? func = null)
    {
        var list = new List<Assembly>();
        var deps = DependencyContext.Default!;
        var libs = deps.RuntimeLibraries.AsEnumerable();
        if (func != null)
        {
            libs = libs.Where(func);
        }

        foreach (var lib in libs)
        {
            var name = lib.RuntimeAssemblyGroups.FirstOrDefault()?.AssetPaths.FirstOrDefault()?.Replace(".dll", "") ??
                       lib.Name;
            name = name.Split('/').Last();
            var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(name));
            // var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
            list.Add(assembly);
        }

        return list;
    }

    /// <summary>
    /// 获取包含指定名称的程序集
    /// </summary>
    public static List<Assembly> GetAssemblies(string value)
    {
        return GetAssemblies(lib => lib.Name.Contains(value));
    }

    public static List<Assembly> GetAssemblies(List<string> assemblyNames)
    {
        return GetAssemblies(lib => assemblyNames.Contains(lib.Name));
    }

    #endregion

    #region 获取类型

    /// <summary>
    /// 获取包含指定类型的类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static List<Type> GetTypes(Type type, bool inherit = false) =>
        GetVinTypes.Where(m =>
            m.IsClass && (m.GetInterfaces().Contains(type) || m == type || m.IsDefined(type, inherit) ||
                          m.BaseType == type)).ToList();


    public static List<Type> GetTypes<T>(bool inherit = false) where T : class
    {
        return GetTypes(typeof(T), inherit);
    }

    public static List<Type> GetTypesByAttribute<T>() where T : Attribute
    {
        // return GetVinTypes.Where(m => m.GetCustomAttribute<T>() != null).ToList();
        return GetVinTypes.Where(m => m.IsDefined(typeof(T), true)).ToList();
    }

    #endregion

    #region 获取程序集中的资源

    private static Assembly GetCallingAssembly()
    {
        return Assembly.GetEntryAssembly()!;
    }

    public static string GetAssemblyName(Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        return assembly.GetName().Name ?? "";
    }

    public static List<string> GetManifestResourceNames(Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        return assembly.GetManifestResourceNames().ToList();
    }

    public static Stream? GetManifestResourceStream(string name, Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        return assembly.GetManifestResourceStream($"{GetAssemblyName(assembly)}.{name.Replace("/", ".")}");
    }

    public static async Task<Stream?> GetManifestResourceStreamAsync(string name, Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        return await Task.Run(() =>
            assembly.GetManifestResourceStream($"{GetAssemblyName(assembly)}.{name.Replace("/", ".")}"));
    }

    public static string? GetManifestResourceString(string name, Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        using var stream = GetManifestResourceStream(name, assembly);
        if (stream == null)
        {
            return null;
        }

        using var reader = new StreamReader(stream);
        return reader.ReadToEnd();
    }

    public static async Task<string?> GetManifestResourceStringAsync(string name, Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        using var stream = GetManifestResourceStream(name, assembly);
        if (stream == null)
        {
            return null;
        }

        using var reader = new StreamReader(stream);
        return await reader.ReadToEndAsync();
    }

    public static ManifestResourceInfo? GetManifestResourceInfo(string name, Assembly? assembly = null)
    {
        assembly ??= GetCallingAssembly();

        return assembly.GetManifestResourceInfo($"{GetAssemblyName(assembly)}.{name.Replace("/", ".")}");
    }

    #endregion
}